



import {shader} from './shader_class';
import {vertice_firework_source, fragment_firework_source, vertice_rect_source, fragment_rect_source, fragment_blur_downsample_source, fragment_blur_upsample_source} from './shader';
import {VAO_rect_generate} from './vao_rect';
import {VAO_firework_generate} from './vao_fireworks';
import {rotate_matrix, mul_matrix} from './matrix';

import {render_all, color_data} from './react_user_element'


let range_value:number = 0.03;
function change_range(num:number)
{
	range_value = num;
}


let array_colorlists : Array<color_data> = [
	new color_data(0, 4, 1, 1),
	new color_data(1, 1, 4, 1)
];
function change_color_lists(color_data_inst:color_data[])
{
	array_colorlists = [...color_data_inst];
}


function main() {

	render_all(change_color_lists, change_range);
	
	//const canvas = document.querySelector("#glcanvas") as HTMLCanvasElement;
	if(!canvas)
	{
		return;
	}
	const gl = canvas.getContext("webgl2");


	if (!gl) {
		alert("无法初始化 WebGL2，你的浏览器、操作系统或硬件等可能不支持 WebGL2。");
		return;
	}

	gl.viewport(0, 0, screen_width, screen_height);

	gl.clearColor(0.0, 0.0, 0.0, 1.0);

	gl.clear(gl.COLOR_BUFFER_BIT);


	gl.enable(gl.DEPTH_TEST); //启用 深度检测
	gl.depthFunc(gl.LEQUAL); //深度缓冲值 小于等于时 绘制像素

	const default_framebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);

	//使用拓展
	const ext = gl.getExtension("EXT_color_buffer_float");


	//用于HDR的帧缓冲
	let FBO_hdr_full_size = gl.createFramebuffer();
	let tex_hdr_full_size = gl.createTexture();
	//纹理
	gl.bindTexture(gl.TEXTURE_2D, tex_hdr_full_size);
	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, screen_width, screen_height, 0, gl.RGBA, gl.FLOAT, null);//高动态范围格式
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
	//深度缓冲
	let dep_hdr_full_size = gl.createRenderbuffer();
	gl.bindRenderbuffer(gl.RENDERBUFFER, dep_hdr_full_size);
	gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, screen_width, screen_height);

	gl.bindFramebuffer(gl.FRAMEBUFFER, FBO_hdr_full_size);
	gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_hdr_full_size, 0);
	gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, dep_hdr_full_size);
	if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
		alert("Framebuffer not complete!");
	}


	const num_framebuffer = 4;//dual blur算法层数
	//用于dual blur的帧缓冲
	let FBO_hdr : any[] = new Array();//帧缓冲对象
	let tex_hdr : any[] = new Array();//纹理对象
	let array_span_pixel : number[] = new Array();
	let array_buffer_size : number[] = new Array();
	let buffer_width:number = screen_width;
	let buffer_height:number = screen_height;
	for (let i = 0; i < num_framebuffer; i += 1) {
		//用于HDR的帧缓冲
		FBO_hdr[i] = gl.createFramebuffer();
		tex_hdr[i] = gl.createTexture();
		//纹理
		gl.bindTexture(gl.TEXTURE_2D, tex_hdr[i]);
		gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, buffer_width, buffer_height, 0, gl.RGBA, gl.FLOAT, null);//*********************
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
		//深度缓冲
		let dep_hdr = gl.createRenderbuffer();
		gl.bindRenderbuffer(gl.RENDERBUFFER, dep_hdr);
		gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, buffer_width, buffer_height);

		gl.bindFramebuffer(gl.FRAMEBUFFER, FBO_hdr[i]);
		gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_hdr[i], 0);
		gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, dep_hdr);
		if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
			alert("Framebuffer not complete!");
		}
		//gl.bindFramebuffer(gl.FRAMEBUFFER, default_framebuffer);

		array_span_pixel[2 * i] = 1.0 / buffer_width;
		array_span_pixel[2 * i + 1] = 1.0 / buffer_height;

		array_buffer_size[2 * i] = buffer_width;
		array_buffer_size[2 * i + 1] = buffer_height;

		buffer_width /= 2;
		buffer_height /= 2;
	}





	//编译链接着色器
	//const shaderProgram_ground = initShaderProgram(gl, vertice_ground_source, fragment_ground_source);
	const shaderProgram_firework = new shader(gl, vertice_firework_source, fragment_firework_source);
	const shaderProgram_rect = new shader(gl, vertice_rect_source, fragment_rect_source);
	const shaderProgram_downsample = new shader(gl, vertice_rect_source, fragment_blur_downsample_source);
	const shaderProgram_upsample = new shader(gl, vertice_rect_source, fragment_blur_upsample_source);

	//firework顶点属性
	const programInfo_firework = {
		program: shaderProgram_firework.shaderProgram,
		attribLocations: {
			vertexPosition: gl.getAttribLocation(shaderProgram_firework.shaderProgram, "aPos"),
			instanceDirection: gl.getAttribLocation(shaderProgram_firework.shaderProgram, "instance_direction"),
		},
		uniformLocations: {
			oneMatrix: gl.getUniformLocation(shaderProgram_firework.shaderProgram, "pers_and_rotate"),//合并透视和旋转矩阵

			rotate_cMatrix: gl.getUniformLocation(shaderProgram_firework.shaderProgram, "rota_c"),
			timeFloat: gl.getUniformLocation(shaderProgram_firework.shaderProgram, "time_fire"),
			randColor: gl.getUniformLocation(shaderProgram_firework.shaderProgram, "color_firework"),
			randVelocity: gl.getUniformLocation(shaderProgram_firework.shaderProgram, "rand_velocity"),
			randPosition: gl.getUniformLocation(shaderProgram_firework.shaderProgram, "rand_position"),
			randExplode: gl.getUniformLocation(shaderProgram_firework.shaderProgram, "rand_explode"),
			traceSize: gl.getUniformLocation(shaderProgram_firework.shaderProgram, "size"),
			seed: gl.getUniformLocation(shaderProgram_firework.shaderProgram, "seed"),
		},
	};

	//rect顶点属性
	const programInfo_rect = {
		program: shaderProgram_rect.shaderProgram,
		attribLocations: {
			vertexPosition: gl.getAttribLocation(shaderProgram_rect.shaderProgram, "aPos_n_texcoord"),
		},
		uniformLocations: {
			hdrTexture: gl.getUniformLocation(shaderProgram_rect.shaderProgram, "Tex_hdr"),
			blurTexture: gl.getUniformLocation(shaderProgram_rect.shaderProgram, "Tex_blur"),
		},
	};

	//downsample顶点属性
	const programInfo_downsample = {
		program: shaderProgram_downsample.shaderProgram,
		attribLocations: {
			vertexPosition: gl.getAttribLocation(shaderProgram_downsample.shaderProgram, "aPos_n_texcoord"),
		},
		uniformLocations: {
			hdrTexture: gl.getUniformLocation(shaderProgram_downsample.shaderProgram, "Tex_hdr"),
			spanPixel: gl.getUniformLocation(shaderProgram_downsample.shaderProgram, "span_pixel"),
		},
	};

	//upsample顶点属性
	const programInfo_upsample = {
		program: shaderProgram_upsample.shaderProgram,
		attribLocations: {
			vertexPosition: gl.getAttribLocation(shaderProgram_upsample.shaderProgram, "aPos_n_texcoord"),
		},
		uniformLocations: {
			hdrTexture: gl.getUniformLocation(shaderProgram_upsample.shaderProgram, "Tex_hdr"),
			spanPixel: gl.getUniformLocation(shaderProgram_upsample.shaderProgram, "span_pixel"),
		},
	};



	const num_vertices = 8;//单个烟花粒子顶点数量
	const num_firework = 5;//烟花粒子数量 球形半周长
	const VAO_firework = VAO_firework_generate(gl, programInfo_firework, num_vertices, num_firework);
	const VAO_rect = VAO_rect_generate(gl, programInfo_rect);


	//激活纹理单元
	// gl.activeTexture(gl.TEXTURE0);
	// gl.bindTexture(gl.TEXTURE_2D, tex_hdr);
	// gl.useProgram(programInfo_rect.program);
	// gl.uniform1i(programInfo_rect.uniformLocations.hdrTexture, 0);

	

	let then = 0;

	function render(now:number) {
	
		let rand_firework:number = Math.random();

		if (rand_firework < range_value) {//判断是否小于门限

			add_firework(Math.random());
		}
		//para_firework 过时处理
		if (para_firework[6] > 2) {
			for (let i = 0; i < 7; i += 1) {
				para_firework.shift();
			}
		}


		now *= 0.001; // convert to seconds
		const deltaTime = now - then;
		then = now;

		const perspec = [
			1.0, 0.0, 0.0, 0.0, 0.0, screen_width / screen_height, 0.0, 0.0, 0.0, 0.0, -1.001, -1.0, 0.0, 0.0, -0.21, 0.0];

		const xz_sin = Math.sin(angle_xz);
		const xz_cos = Math.cos(angle_xz);
		const y_sin = Math.sin(angle_y);
		const y_cos = Math.cos(angle_y);
		const rotate = [xz_cos, y_sin * xz_sin, y_cos * xz_sin, 0.0,
			0.0, y_cos, -y_sin, 0.0,
			-xz_sin, y_sin * xz_cos, y_cos * xz_cos, 0.0,
			0.0, 0.0, 0.0, 1.0];
		const rotate_contra = [xz_cos, y_sin * xz_sin, -y_cos * xz_sin, 0.0,
			0.0, y_cos, y_sin, 0.0,
			xz_sin, -y_sin * xz_cos, y_cos * xz_cos, 0.0,
			0.0, 0.0, 0.0, 1.0];

		let pers_and_rotate = mul_matrix(perspec, rotate);

		//从hdr帧缓冲渲染
		gl.bindFramebuffer(gl.FRAMEBUFFER, FBO_hdr_full_size);
		gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

		//渲染firework
		gl.useProgram(programInfo_firework.program);
		gl.uniformMatrix4fv(programInfo_firework.uniformLocations.oneMatrix, false, new Float32Array(pers_and_rotate));
		//gl.uniformMatrix4fv(programInfo_firework.uniformLocations.rotateMatrix, false, new Float32Array(rotate));
		gl.uniformMatrix4fv(programInfo_firework.uniformLocations.rotate_cMatrix, false, new Float32Array(rotate_contra));
		//gl.uniform1f(programInfo_firework.uniformLocations.timeFloat, now % 2);
		gl.bindVertexArray(VAO_firework);
		//循环渲染不同firework
		for (let i = 0; i < para_firework.length; i += 7) {
			gl.uniform1f(programInfo_firework.uniformLocations.timeFloat, para_firework[i + 6]);
			gl.uniform3f(programInfo_firework.uniformLocations.randColor, para_firework[i], para_firework[i + 1], para_firework[i + 2]);
			gl.uniform1f(programInfo_firework.uniformLocations.randVelocity, para_firework[i + 3]);
			gl.uniform1f(programInfo_firework.uniformLocations.randPosition, para_firework[i + 4]);
			gl.uniform1f(programInfo_firework.uniformLocations.randExplode, para_firework[i + 5]);

			gl.uniform1f(programInfo_firework.uniformLocations.seed, para_firework[i + 4] * para_firework[i + 5] * 24567.24);//随机数种子
			gl.uniform1f(programInfo_firework.uniformLocations.traceSize, 1.0);



			if (para_firework[i + 6] < 1) {
				gl.drawElements(gl.TRIANGLES, (num_vertices - 2) * 3, gl.UNSIGNED_SHORT, 0);
			} else {
				gl.drawElementsInstanced(gl.TRIANGLES, (num_vertices - 2) * 3, gl.UNSIGNED_SHORT, 0, num_firework * (num_firework - 1) * 2);

				//轨迹 在特定时间下会有轨迹
				const num_trace = 10;
				const period_trace = 2;
				let time_trace = Math.floor(para_firework[i + 6] * 10.0) / 10.0;

				for (let i_trace = 0; time_trace > 1.0 && i_trace < period_trace; i_trace += 1, time_trace -= 1.0 / num_trace) {
					gl.uniform1f(programInfo_firework.uniformLocations.timeFloat, time_trace);
					let traceSize = (period_trace - (para_firework[i + 6] - time_trace)) / period_trace;
					gl.uniform1f(programInfo_firework.uniformLocations.traceSize, traceSize);
					gl.drawElementsInstanced(gl.TRIANGLES, 3, gl.UNSIGNED_SHORT, 0, num_firework * (num_firework - 1) * 2);

				}

			}




			//时间更新
			para_firework[i + 6] += deltaTime;
		}



		//模糊流程
		//下采样
		for (let i = 0; i < num_framebuffer - 1; i += 1) {
			gl.viewport(0, 0, array_buffer_size[2 * (i + 1)], array_buffer_size[2 * (i + 1) + 1]);
			//绑定默认帧缓冲
			gl.bindFramebuffer(gl.FRAMEBUFFER, FBO_hdr[i + 1]);
			gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

			gl.useProgram(programInfo_downsample.program);
			gl.bindVertexArray(VAO_rect);

			gl.activeTexture(gl.TEXTURE0);
			if (i == 0) {
				gl.bindTexture(gl.TEXTURE_2D, tex_hdr_full_size);
			} else {
				gl.bindTexture(gl.TEXTURE_2D, tex_hdr[i]);
			}

			// gl.useProgram(programInfo_rect.program);
			gl.uniform1i(programInfo_downsample.uniformLocations.hdrTexture, 0);
			gl.uniform2f(programInfo_downsample.uniformLocations.spanPixel, array_span_pixel[2 * i], array_span_pixel[2 * i + 1]);

			gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
		}
		//上采样
		for (let i = num_framebuffer - 1; i > 0; i -= 1) {
			gl.viewport(0, 0, array_buffer_size[2 * (i - 1)], array_buffer_size[2 * (i - 1) + 1]);
			//绑定默认帧缓冲
			gl.bindFramebuffer(gl.FRAMEBUFFER, FBO_hdr[i - 1]);
			gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

			gl.useProgram(programInfo_upsample.program);
			gl.bindVertexArray(VAO_rect);

			gl.activeTexture(gl.TEXTURE0);
			gl.bindTexture(gl.TEXTURE_2D, tex_hdr[i]);
			// gl.useProgram(programInfo_rect.program);
			gl.uniform1i(programInfo_upsample.uniformLocations.hdrTexture, 0);
			gl.uniform2f(programInfo_upsample.uniformLocations.spanPixel, array_span_pixel[2 * i], array_span_pixel[2 * i + 1]);

			gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
		}


		//绑定默认帧缓冲
		gl.bindFramebuffer(gl.FRAMEBUFFER, default_framebuffer);
		gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
		//渲染rect
		gl.useProgram(programInfo_rect.program);
		gl.bindVertexArray(VAO_rect);

		gl.activeTexture(gl.TEXTURE0);
		gl.bindTexture(gl.TEXTURE_2D, tex_hdr_full_size);
		gl.uniform1i(programInfo_rect.uniformLocations.blurTexture, 0);

		gl.activeTexture(gl.TEXTURE1);
		gl.bindTexture(gl.TEXTURE_2D, tex_hdr[0]);
		gl.uniform1i(programInfo_rect.uniformLocations.hdrTexture, 1);

		gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);


		requestAnimationFrame(render);
	}

	requestAnimationFrame(render);
}

let angle_xz = 0;
let angle_y = 0;
let x = 0;
let y = 0;



//随机参数:颜色rgb、初速度、方位、爆炸力度、时间
let para_firework : number[] = new Array();

function handle_mousedown(event:any) {
	const x_current = event.offsetX / screen_width;

	//增加firework
	add_firework(x_current);
}





function add_firework(position:number) :void{
	//增加firework
	let color_firework:number[] = [];
	let color_rand:number = Math.random();//控制颜色选择的随机数
	let color_index:number = Math.floor(color_rand * (array_colorlists.length));

	if (array_colorlists.length !== 0) {
		color_firework[0] = array_colorlists[color_index].color_r;
		color_firework[1] = array_colorlists[color_index].color_g;
		color_firework[2] = array_colorlists[color_index].color_b;

		para_firework.push(color_firework[0], color_firework[1], color_firework[2]);

		para_firework.push(Math.random());
		para_firework.push(position);
		para_firework.push(Math.random());
		para_firework.push(0);
	}
}



function handle_touchstart(event:any) {

	let touches = event.targetTouches;
	let num_touches = touches.length;

	for (let i = 0; i < num_touches; i += 1) {
		add_firework(touches[i].clientX / screen_width);
	}
}



//像素大小初始化
const screen_width = window.innerWidth;
const screen_height = window.innerHeight;
const canvas = document.querySelector("#glcanvas") as HTMLCanvasElement;
if(canvas)
{
	canvas.width = screen_width;
	canvas.height = screen_height;
}
	





document.querySelector("#glcanvas").addEventListener("mousedown", handle_mousedown);

document.querySelector("#glcanvas").addEventListener("touchstart", handle_touchstart);//触摸事件


//事件监听
document.addEventListener("DOMContentLoaded", main);